# **********************************************************************
# *                   (c) SEGGER Microcontroller GmbH                  *
# *                        The Embedded Experts                        *
# *                           www.segger.com                           *
# **********************************************************************
# ----------------------------------------------------------------------
# Purpose : CMake toolchain file for SEGGER Toolchain (ARM and RISC-V)
# ----------------------------------------------------------------------
#
# Notes
#   [1] Mandatory CLI option:
#         i. Either:
#                -DCMAKE_TOOLCHAIN_FILE=<TOOLCHAIN-FILE-PATH>/segger-toolchain.cmake
#            Or:
#                --toolchain <TOOLCHAIN-FILE-PATH>/segger-toolchain.cmake
#         ii. -DCMAKE_SYSTEM_PROCESSOR=<architecture/cpu>
#
#   [2] Optional CLI option:
#         -DSEGGER_TOOLCHAIN_PKG_ROOT=<root folder of your Embedded Studio Installation>
#         -DSEGGER_USE_GCC=1          : force usage of GCC compiler
#         -DSEGGER_USE_GNU_LINKER=1   : (only if -DSEGGER_USE_GCC==1) - force use of GNU linker instead of SEGGER linker.
#
# Literature
#   [1] https://cmake.org/cmake/help/latest/
#
# ---------------------------END-OF-HEADER------------------------------
#
cmake_minimum_required(VERSION 3.22)
message(DEBUG "********** SEGGER TOOLCHAIN START: CMAKE_SYSTEM_PROCESSOR = ${CMAKE_SYSTEM_PROCESSOR} ************")
set(SEGGER_CMAKE_TOOLCHAIN_VERSION 1.2.0 CACHE STRING "The version of the SEGGER CMake toolchain package.")
#------------------------------------------------------------------------------------------------------------------------------------------------
#
# Some sanity checks to start off with...
#
#------------------------------------------------------------------------------------------------------------------------------------------------
macro(__segger_forward_try_compile_platform_vars _var)
  if(NOT "${_var}" IN_LIST CMAKE_TRY_COMPILE_PLATFORM_VARIABLES)
    list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES ${_var})  # Enforce propagation of this variable into "test" project (e.g. for ABI tests).
  endif()
endmacro()
#------------------------------------------------------------------------------------------------------------------------------------------------
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
  message(FATAL_ERROR "CMAKE_SYSTEM_PROCESSOR undefined. Please specify it on commandline using -DCMAKE_SYSTEM_PROCESSOR=<value> or in your CMakeLists.txt !")
else()
  __segger_forward_try_compile_platform_vars(CMAKE_SYSTEM_PROCESSOR)
endif()

#------------------------------------------------------------------------------------------------------------------------------------------------
#
# Specify OS dependent search paths for SES toolchain.
#
if(CMAKE_HOST_WIN32)
  set(_EXEC_SUFFIX ".exe")
  cmake_path(SET _SEARCH_PATH NORMALIZE "C:/Program Files/SEGGER")
else()
  set(_EXEC_SUFFIX "")
  if (APPLE)
    cmake_path(SET _SEARCH_PATH NORMALIZE "/Applications/SEGGER")
  else()
    cmake_path(SET _SEARCH_PATH NORMALIZE "/opt/SEGGER")

    # Check whether we're running under WSL. There seems to be no 100% reliable method for checking this. But this method may be the best.
    # Read /proc/sys/fs/binfmt_misc/WSLInterop and check for the magic 4d5a
    file(TO_CMAKE_PATH "/proc/sys/fs/binfmt_misc/WSLInterop" _wslinterop_path)
    if(EXISTS ${_wslinterop_path})
      file(READ ${_wslinterop_path} _wslinterop_content)
      message(DEBUG "WSLInterop content is:\n${_wslinterop_content}")
      if(${_wslinterop_content} MATCHES "magic\ +4d5a")
        message(DEBUG "Found magic sequence in content.")
        set(_EXEC_SUFFIX ".exe")
        cmake_path(SET _SEARCH_PATH NORMALIZE "/mnt/c/Program Files/SEGGER")
      endif()
    endif()
  endif()
endif()
message(DEBUG "_SEARCH_PATH = ${_SEARCH_PATH}")

#
# In case SEGGER_TOOLCHAIN_PKG_ROOT has not been specified, browse through search path to identify latest version of the SES toolchain.
#
# TODO: how to handle the case that we find multiple installations of cc-segger. Prompt the user to choose the desired one?
#       currently we just choose the last in the (sorted) list, which should correspond to the newest version.
if(NOT DEFINED SEGGER_TOOLCHAIN_PKG_ROOT)
  file(GLOB_RECURSE _TC_LIST ${_SEARCH_PATH}/*/cc-segger${_EXEC_SUFFIX})
  message(DEBUG "_TC_LIST = ${_TC_LIST}")
  list(LENGTH _TC_LIST length)
  message(DEBUG "length = ${length}")
  if(length GREATER 0)
    list(GET _TC_LIST -1 _TC)                              # Make sure we do not have a list as return value
    cmake_path(GET _TC PARENT_PATH _TC)                    # Get directory
    cmake_path(GET _TC PARENT_PATH SEGGER_TOOLCHAIN_PKG_ROOT)  # Strip /bin/
  endif()
endif()
message(DEBUG "SEGGER_TOOLCHAIN_PKG_ROOT = ${SEGGER_TOOLCHAIN_PKG_ROOT}")
__segger_forward_try_compile_platform_vars(SEGGER_TOOLCHAIN_PKG_ROOT)
set(SEGGER_TOOLCHAIN_PKG_ROOT ${SEGGER_TOOLCHAIN_PKG_ROOT} CACHE STRING "The path to the utilized SEGGER compiler/toolchain installation.")

#------------------------------------------------------------------------------------------------------------------------------------------------
#
# Handle SEGGER_USE_GCC and SEGGER_USE_GNU_LINKER arguments
#
set(_SEGGER_USE_GCC 0)
set(_SEGGER_USE_GNU_LINKER 0)
if(DEFINED SEGGER_USE_GCC AND SEGGER_USE_GCC GREATER 0)
  __segger_forward_try_compile_platform_vars(SEGGER_USE_GCC)
  set(_SEGGER_USE_GCC 1)
  if(DEFINED SEGGER_USE_GNU_LINKER AND SEGGER_USE_GNU_LINKER GREATER 0)
    __segger_forward_try_compile_platform_vars(SEGGER_USE_GNU_LINKER)
    set(_SEGGER_USE_GNU_LINKER 1)
  endif()
endif()

#------------------------------------------------------------------------------------------------------------------------------------------------
set(CMAKE_SYSTEM_NAME Generic)        # https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html
#------------------------------------------------------------------------------------------------------------------------------------------------
# Set the binaries for the compiler-calls.
# NOTES:
#    - the SEGGER driver defaults to "GCC" compiler mode when called as "cc" or "cc++".
#    - for SEGGER compiler mode, we need to call the driver with "-segger" switch.
#    - by default we use SEGGER linker for both GNU and SEGGER modes, since typically the project will maintain only 1 linker script type (SEGGER).
#      Override is possible by setting -DSEGGER_USE_GNU_LINKER=1 if and only if also setting -DSEGGER_USE_GCC=1.
set(CMAKE_ASM_COMPILER          ${SEGGER_TOOLCHAIN_PKG_ROOT}/bin/cc${_EXEC_SUFFIX}   )
set(CMAKE_C_COMPILER            ${SEGGER_TOOLCHAIN_PKG_ROOT}/bin/cc${_EXEC_SUFFIX}   )
set(CMAKE_CXX_COMPILER          ${SEGGER_TOOLCHAIN_PKG_ROOT}/bin/cc++${_EXEC_SUFFIX} )

if(${_SEGGER_USE_GNU_LINKER} EQUAL 0)
  set(CMAKE_EXE_LINKER_FLAGS_INIT "-segger-ld")
endif()

if(${_SEGGER_USE_GCC} EQUAL 1)
  set(CMAKE_ASM_COMPILER_ID GNU)      # Set this otherwise we get "The ASM compiler identification is unknown"
                                      # and "cmake_modules/Platform/Generic-GNU-ASM-<processor-name>.cmake" does not get included.
  #------------------------------------------------------------------------------------------------------------------------------------------------
  set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY CACHE INTERNAL "")  # Avoid issue during the ABI test, since otherwise CMake will try to link 
                                                                       # an executable (instead of just creating a library) without having 
                                                                       # a linker script and that would fail.
else()
  set(CMAKE_ASM_FLAGS_INIT   " -segger")
  set(CMAKE_C_FLAGS_INIT     " -segger")
  set(CMAKE_CXX_FLAGS_INIT   " -segger")
endif()
#------------------------------------------------------------------------------------------------------------------------------------------------
if( NOT DEFINED SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID )
  include(Compiler/SEGGERClang OPTIONAL) # Compiler/SEGGERClang.cmake defines __compiler_seggerclang
  if(NOT COMMAND __compiler_seggerclang)
    set(SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID FALSE CACHE INTERNAL "This is set to TRUE, if the current CMake binary has built-in SEGGER compiler support. Otherwise it is set to FALSE.")
  else()
    set(SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID TRUE CACHE INTERNAL "This is set to TRUE, if the current CMake binary has built-in SEGGER compiler support. Otherwise it is set to FALSE.")
  endif()
  __segger_forward_try_compile_platform_vars(SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID)
endif()
#------------------------------------------------------------------------------------------------------------------------------------------------
# Append the extra segger_cmake_modules folder to the CMAKE_MODULE_PATH if the current CMake binary does not have built-in SEGGER support.
cmake_path(SET _SEGGER_CMAKE_MODULES_FOLDER NORMALIZE ${CMAKE_CURRENT_LIST_DIR})
cmake_path(APPEND _SEGGER_CMAKE_MODULES_FOLDER "${_SEGGER_CMAKE_MODULES_FOLDER}" "segger_cmake_modules")

if(NOT SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID AND NOT "${_SEGGER_CMAKE_MODULES_FOLDER}" IN_LIST CMAKE_MODULE_PATH)
  list(APPEND CMAKE_MODULE_PATH "${_SEGGER_CMAKE_MODULES_FOLDER}")
  include(Compiler/SEGGERClang)
endif()
unset(_SEGGER_CMAKE_MODULES_FOLDER)
#------------------------------------------------------------------------------------------------------------------------------------------------
if( ${_SEGGER_USE_GCC} EQUAL 1 AND NOT DEFINED SEGGER_CMAKE_INIT_GNU_BINUTILS)
  # For GNU-mode we want to force usage of the binutils that come with the SEGGER compiler tool package.
  if(DEFINED _CMAKE_PROCESSING_LANGUAGE)
    set(_orig_CMAKE_PROCESSING_LANGUAGE ${_CMAKE_PROCESSING_LANGUAGE})
  endif()

  set(_CMAKE_PROCESSING_LANGUAGE "DUMMY")    # Force "DUMMY" to avoid fatal-error in Compiler/SEGGERClang-FindBinUtils
  include(Compiler/SEGGERClang-FindBinUtils)      # If SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID is TRUE, then SEGGER-FindBinUtils that is part of the cmake distribution will be used. Else use the locally packaged one.

  if(DEFINED _orig_CMAKE_PROCESSING_LANGUAGE)# restore previous value
    set(_CMAKE_PROCESSING_LANGUAGE ${_orig_CMAKE_PROCESSING_LANGUAGE})
    unset(_orig_CMAKE_PROCESSING_LANGUAGE)
  else()
    unset(_CMAKE_PROCESSING_LANGUAGE)
  endif()  

  set(SEGGER_CMAKE_INIT_GNU_BINUTILS TRUE CACHE INTERNAL "")
endif()
#------------------------------------------------------------------------------------------------------------------------------------------------
if(NOT DEFINED SEGGER_CMAKE_INIT_ID_WORKAROUND)
  set(SEGGER_CMAKE_INIT_ID_WORKAROUND TRUE CACHE INTERNAL "")
  __segger_forward_try_compile_platform_vars(SEGGER_CMAKE_INIT_ID_WORKAROUND)
  if(NOT SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID)
    # === START: Workarounds for missing builtin-in compiler-ID detection. ===
    #
    # CMake will try to compile a test program to make sure the compiler works.
    # and to get some version information from preprocessor defines.
    # Without the following code, CMake will use add_executable() for the test program.
    # This will throw the error 'The C compiler "XXX" is not be able to compile a simple test program.'.
    # Building the test program fails to link as it doesn't have a custom linker file.
    #------------------------------------------------------------------------------------------------------------------------------------------------
    if(${_SEGGER_USE_GCC} EQUAL 0)
      set(CMAKE_C_COMPILER_ID   SEGGERClang CACHE INTERNAL "")
      set(CMAKE_CXX_COMPILER_ID SEGGERClang CACHE INTERNAL "")
      set(CMAKE_ASM_COMPILER_ID SEGGERClang CACHE INTERNAL "")
    endif()
    # === END: Workarounds for missing builtin-in compiler-ID detection. ===
  endif()
endif()

#-- START: Cleanup ----------------------------------------------------------------------------------------------------------------------------------------------
unset(_SEARCH_PATH)
unset(_EXEC_SUFFIX)
unset(_SEGGER_USE_GCC)
unset(_SEGGER_USE_GNU_LINKER)
#-- END: Cleanup ----------------------------------------------------------------------------------------------------------------------------------------------
# **************************** end of file ****************************/
